/**************************************************************************************************
	$Header: /pub/cvsroot/mydns/lib/conf.c,v 1.7 2002/07/04 14:20:15 bboy Exp $
	conf.c: Routines to read the configuration file.

	Copyright (C) 2002  Don Moore <bboy@bboy.net>

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at Your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
**************************************************************************************************/

#include "mydnsutil.h"


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	CONF_TRIM_COMMENTS
	Trim comments from a line, obeying escape characters.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
conf_trim_comments(s)
	unsigned char *s;
{
	register unsigned char *c;

	for (c = s; *c; c++)
	{
		if (*c == '\\')
			c++;
		else if (c[0] == '#' || (c[0] == '/' && (c[1] == '*' || c[1] == '/')))
		{
			*c = '\0';
			return;
		}
	}
}
/*--- conf_trim_comments() ----------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	CONF_GET_OPTION
	Parses the provided line, splitting a "name = value" pair, and escapes text.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
conf_get_option(str, namep, valuep)
	unsigned char *str;		/* String to parse */
	unsigned char **namep;	/* Where to store `name' */
	unsigned char **valuep;	/* Where to store `value' */
{
	register unsigned char *c;
	register int  got_equal = 0;								/* Have we found a valid equal sign yet? */
	register size_t nlen, vlen;
	unsigned char *name, *value;

	Assert(namep && valuep);

	name = xmalloc(1 * sizeof(unsigned char));
	value = xmalloc(1 * sizeof(unsigned char));
	nlen = vlen = (size_t)0;
	name[0] = '\0';
	value[0] = '\0';

	*namep = *valuep = (unsigned char *)NULL;

	for (c = str; *c; c++)
	{
		if (*c == '=' && !got_equal)
		{
			got_equal = 1;
			continue;
		}

		if (*c == '\\')
			c++;

		if (!got_equal)
		{
			name = realloc(name, (nlen + 2) * sizeof(unsigned char));
			name[nlen++] = *c;
			name[nlen] = '\0';
		}
		else
		{
			value = realloc(value, (vlen + 2) * sizeof(unsigned char));
			value[vlen++] = *c;
			value[vlen] = '\0';
		}
	}
	strtrim(name);
	strtolower(name);
	strtrim(value);

	if (strlen(name))
		*namep = name;
	else
		free(name);

	if (strlen(value))
		*valuep = value;
	else
		free(value);
}
/*--- conf_get_option() -------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	CONF_SET
	Sets a configuration value.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void
conf_set(confp, name, value)
	CONF **confp;	/* Config option list */
	unsigned char *name;		/* Option name */
	unsigned char *value;	/* Option value */
{
	CONF *conf = *confp;											/* Start of list */
	CONF *new;														/* New item to add */
	register CONF *c;

	if (!name)
		return;

	/* Try to find the specified option in the current list; don't replace if present */
	for (c = conf; c; c = c->next)
		if (c->name && !memcmp(c->name, name, strlen(c->name)))
			return;

	/* Add new item to array */
	new = (CONF *)xmalloc(sizeof(CONF));
	new->name = xstrdup(name);
	new->value = value ? (unsigned char *)xstrdup(value) : (unsigned char *)NULL;
	new->next = conf;
	conf = new;

	*confp = conf;
}
/*--- conf_set() --------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	CONF_GET
	Returns the value associated with the specified name.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
unsigned char *
conf_get(confp, name)
	CONF **confp;		/* Config option list */
	unsigned char *name;			/* Option name to retrieve */
{
	CONF *conf = *confp;											/* Start of list */
	register CONF *c;

	for (c = conf; c; c = c->next)
		if (!memcmp(c->name, name, strlen(c->name)))
			return (c->value);
	return (unsigned char *)NULL;
}
/*--- conf_get() --------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	CONF_LOAD
	Load the MyDNS configuration file.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void
conf_load(confp, filename)
	CONF **confp;
	const unsigned char *filename;
{
	FILE	*fp;														/* Input file pointer */
	unsigned char	linebuf[BUFSIZ];							/* Input line buffer */
	unsigned char	*name, *value;								/* Name and value from `linebuf' */
	CONF	*conf = *confp;										/* Config list */
	struct stat st;												/* File stat for conf file */

	/* If the config file is not present at all, that's not an error */
	if (stat(filename, &st))
	{
		if (errno == ENOENT)
			return;
		return (void)Warn("%s", filename);
	}

	/* XXX: Check permissions and output warning if too promiscuous */

	/* Read config file */
	if ((fp = fopen(filename, "r")))
	{
		while (fgets(linebuf, sizeof(linebuf), fp))
		{
			conf_trim_comments(linebuf);
			conf_get_option(linebuf, &name, &value);
			if (name)
			{
				conf_set(&conf, name, value);
				free(name);
			}
			if (value)
				free(value);
		}
		fclose(fp);
	}
	else
		return (void)Warn("%s", filename);

	*confp = conf;
}
/*--- conf_load() -------------------------------------------------------------------------------*/

/* vi:set ts=3: */
